#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "glad/glad.h"
#include "GLFW/glfw3.h"

#include "shader.hpp"
#include <windows.h>

using namespace std;

GLFWwindow* window;

extern "C" void startHelloCircle(void* phy_fb, int width, int height, int color_bytes, struct EXTERNAL_GFX_OP* gfx_op);
extern "C" void* getUiOfHelloCircle(int* width, int* height, bool force_update);

extern void create_thread(unsigned long* thread_id, void* attr, void *(*start_routine) (void *), void* arg);
extern void thread_sleep(unsigned int milli_seconds);

#define UI_WIDTH 680
#define UI_HEIGHT 512

bool   m_is_pressed = false;

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        //将窗口设置为关闭，跳出循环
        glfwSetWindowShouldClose(window, 1);
    }

    double x, y;
    glfwGetCursorPos(window, &x, &y);

    if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)
    {
        if(m_is_pressed == false)
        {
            m_is_pressed = true;
//            sendTouch2HelloWidgets((int)x, (int)y, m_is_pressed);
            cout << "GLFW_PRESS " << x << "," << y << "\n";
        }
    }

    else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_RELEASE)
    {
        if(m_is_pressed)
        {
            m_is_pressed = false;
//            sendTouch2HelloWidgets((int)x, (int)y, m_is_pressed);
            cout << "GLFW_RELEASE " << x << "," << y << "\n";
        }
    }

}

static void* startWindow(void* param)
{
    (void)param;
    startHelloCircle(calloc(UI_WIDTH * UI_HEIGHT, 4), UI_WIDTH, UI_HEIGHT, 4, NULL);
    return 0;
}

static bool force_update = true;
static void* global_fb = NULL;

static void* guiUpdate(void* param)
{
    (void)param;
    while(true)
    {
        void* fb = getUiOfHelloCircle(NULL, NULL, force_update);

        if (fb) {
            global_fb = fb;
            force_update = false;
        }
        glfwPostEmptyEvent();

        thread_sleep(30);
    }
    return 0;
}


// Load the texture using any two methods
GLuint Texture;

int main()
{
    //glfw初始化
    glfwInit();
    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    //glfw创建窗口
    window = glfwCreateWindow(UI_WIDTH, UI_HEIGHT, "Hello3Ddonut", NULL, NULL);

    if (window == NULL)
    {
        printf("创建窗口失败");
        //终止
        glfwTerminate();
        return -1;
    }

    //显示窗口
    glfwMakeContextCurrent(window);

    // glad初始化
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        printf("加载失败");
        return -1;
    }

    // Ensure we can capture the escape key being pressed below
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
    glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, GL_TRUE);

    unsigned long thread_id;
    create_thread(&thread_id, 0, startWindow, 0);

    glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

    // Enable depth test
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    // Accept fragment if it closer to the camera than the former one
    glDepthFunc(GL_LESS);

    GLuint VertexArrayID;
    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);

    // Create and compile our GLSL program from the shaders
    GLuint programID = LoadShaders( "../SimpleVertexShader.vertexshader", "../SimpleFragmentShader.fragmentshader" );

    // Create one OpenGL texture
    glGenTextures(1, &Texture);

    // "Bind" the newly created texture : all future texture functions will modify this texture
    glBindTexture(GL_TEXTURE_2D, Texture);

    // ... nice trilinear filtering ...
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    unsigned long thread_gui_id;
    create_thread(&thread_gui_id, 0, guiUpdate, 0);

    // Our vertices. Tree consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.
    // A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices
    static const GLfloat g_vertex_buffer_data[] = {
        -1.0f,-1.0f, 0.0f,
        1.0f,-1.0f, 0.0f,
        -1.0f, 1.0f, 0.0f,

        1.0f,-1.0f, 0.0f,
        1.0f, 1.0f, 0.0f,
        -1.0f, 1.0f, 0.0f,

    };

    // Two UV coordinatesfor each vertex. They were created with Blender.
    static const GLfloat g_uv_buffer_data[] = { // uv倒置
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,

        1.0f, 1.0f,
        1.0f, 0.0f,
        0.0f, 0.0f,

    };


    GLuint vertexbuffer;
    glGenBuffers(1, &vertexbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

    GLuint uvbuffer;
    glGenBuffers(1, &uvbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_uv_buffer_data), g_uv_buffer_data, GL_STATIC_DRAW);


    // 1rst attribute buffer : vertices
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glVertexAttribPointer(
                0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                0,                  // stride
                (void*)0            // array buffer offset
                );

    // 2nd attribute buffer : colors
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
    glVertexAttribPointer(
                1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
                2,                                // size
                GL_FLOAT,                         // type
                GL_FALSE,                         // normalized?
                0,                                // stride
                (void*)0                          // array buffer offset
                );

    // Use our shader
    glUseProgram(programID);

    // 使用循环达到循环渲染效果
    while (!glfwWindowShouldClose(window))
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        if(global_fb){
            //32 bits framebuffer
            // 画面倒置 uv解决了
            // "Bind" the newly created texture : all future texture functions will modify this texture
            glBindTexture(GL_TEXTURE_2D, Texture);
            // Give the image to OpenGL
            glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, UI_WIDTH, UI_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, (unsigned char*)global_fb);
        }

        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glBindVertexArray(VertexArrayID);
        // Draw the triangle !
        glDrawArrays(GL_TRIANGLES, 0, 2*3); // 3 indices starting at 0 -> 1 triangle

        glFlush();

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

        //自定义输入事件
        processInput(window);

        //输入输出事件,否则无法对窗口进行交互
        glfwWaitEvents();

        //交互缓冲区，否则显示空白
        glfwSwapBuffers(window);
    }

    // Cleanup VBO
    glDeleteBuffers(1, &vertexbuffer);
    glDeleteBuffers(1, &uvbuffer);
    glDeleteVertexArrays(1, &VertexArrayID);
    glDeleteTextures(1, &Texture);
    glDeleteProgram(programID);

    //终止渲染 关闭并清理glfw本地资源
    glfwTerminate();
    return 0;
}
